package com.fourhats.services.impl;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import org.dozer.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.fourhats.dao.PlaceDAO;
import com.fourhats.exception.PlaceException;
import com.fourhats.model.Place;
import com.fourhats.services.PlaceService;
import com.fourhats.services.dto.PlaceDto;

public class PlaceServiceImpl implements PlaceService {

	@Autowired
	private PlaceDAO placeDao;
	
	@Autowired
	private Mapper mapper;
	
	@Transactional
	public List<PlaceDto> getAllEnabledPlaces() throws PlaceException {
		List<Place> places = placeDao.getAllEnabledPlaces();
		
		return getListDto(places);
	}

	@Transactional
	public long addPlace(Place place) throws PlaceException {
		try {
			placeDao.add(place);
			return 1;
		} catch(Exception e){
			return 0;
		}
	}

	@Transactional
	public List<PlaceDto> getNearbyPlaces(double lat, double lng, float distance) throws PlaceException {		
		Map<String, HashMap<String, Double>> boundaries = getBoundaries(lat, lng, distance);
		HashMap<String, Double> north = boundaries.get("north");
		HashMap<String, Double> south = boundaries.get("south");
		HashMap<String, Double> east = boundaries.get("east");
		HashMap<String, Double> west = boundaries.get("west");
		
//		List<Place> places = placeDao.getNearbyPlaces(lat,lng,distance,south.get("lat"), north.get("lat"), west.get("lng"), east.get("lng"));
//		return (List<Long>) CollectionUtils.collect(places, new BeanToPropertyValueTransformer("id"));
		List<Place> places = placeDao.getNearbyPlaces(lat,lng,distance,south.get("lat"), north.get("lat"), west.get("lng"), east.get("lng"));
		return getListDto(places);
	}
	
	@Override
	@Transactional
	public PlaceDto getPlaceById(long id) {
		return this.mapper.map(this.placeDao.getById(id), PlaceDto.class);
	}
	
	private Map<String, HashMap<String, Double>> getBoundaries(double lat, double lng, float distance){
		int earthRadius = 6371;
		Hashtable<String, Double> result = new Hashtable<String, Double>();
		result.put("north", 0.0);
		result.put("south", 180.0);
		result.put("east", 90.0);
		result.put("west", 270.0);
		
		Map<String, HashMap<String, Double>> boundaries = new HashMap<String, HashMap<String, Double>>();
		
		double rLat = Math.toRadians(lat);
		double rLng = Math.toRadians(lng);
		double rAngDist = distance/earthRadius;
		
		Enumeration<String> enumKey = result.keys();
		while (enumKey.hasMoreElements()) {
			String key = (String) enumKey.nextElement();
			double angle = result.get(key);
			
			double rAngle = Math.toRadians(angle);
			double rLatB = Math.asin(Math.sin(rLat) * Math.cos(rAngDist) + Math.cos(rLat) * Math.sin(rAngDist) * Math.cos(rAngle));
			double rLonB = rLng + Math.atan2(Math.sin(rAngle) * Math.sin(rAngDist) * Math.cos(rLat), Math.cos(rAngDist) - Math.sin(rLat) * Math.sin(rLatB));
			
			Map<String, Double> cardinalPosition = new HashMap<String, Double>();
			cardinalPosition.put("lat", Math.toDegrees(rLatB));
			cardinalPosition.put("lng", Math.toDegrees(rLonB));
			
			boundaries.put(key, (HashMap<String, Double>) cardinalPosition);
		}
		
		return boundaries;
	}
	
	private List<PlaceDto> getListDto(List<Place> places){
		List<PlaceDto> dtos = new ArrayList<PlaceDto>();
		for(Place p : places) {
			dtos.add(this.mapper.map(p, PlaceDto.class));
		}
		
		return dtos;
	}
}
